package expressions;

import java.util.ArrayList;
import java.util.List;
import parsing.Parser;

/**
 * Represents an operator that takes one or more arguments and uses them to
 * calculate a value. This is distinct from a ParentExpression in that Operators
 * have precedence.
 */
public abstract class Operator<T> extends ParentExpression<T>
{
    /**
     * Constructs a new argument with some number of arguments.
     */
    public Operator(int arguments)
    {
        super(arguments);
    }

    /**
     * @return the priority of evaluating this operator. Higher priority
     *         operators within an expression get evaluated first (i.e. they get
     *         put lower in the expression tree when parsed)
     */
    public abstract int priority();

    /**
     * Evaluates this expression by evaluating all the children, and combining
     * the results.
     */
    @Override
    public T evaluate(IContext<T> context)
    {
        List<T> results = new ArrayList<T>();

        for (Expression<T> child : children)
            results.add(child.evaluate(context));

        return combineResults(results);
    }

    @Override
    public void parse(Parser<T> parser)
    {
        while (parser.topOperator() instanceof Operator<?>)
        {
            Operator<T> op = (Operator<T>) parser.topOperator();

            if (hasHigherPriority(op))
                parser.buildTopOperator();
            else
                break;
        }

        parser.addTopOperator(this);
    }

    /**
     * Does the other operator have higher priority than this?
     * 
     * Override for e.g. left-associativity.
     */
    protected boolean hasHigherPriority(Operator<T> op)
    {
        return op.priority() > priority();
    }

    @Override
    public void buildTree(Parser<T> parser)
    {
        children.addAll(parser.getArguments(arguments));

        parser.output(this);
    }

    /**
     * Combines the values returned by the children expressions into a value
     * that this expression should return.
     * 
     * @param results
     *            The children values.
     * @return The value of this operator.
     */
    protected abstract T combineResults(List<T> results);
}
